import type * as vscode from 'vscode'
import type * as Ast from '@unified-latex/unified-latex-types'
import type { CmdEnvSuggestion } from './completion/completer/completerutils'

export type FileCache = {
    /** The raw file path of this Cache. */
    filePath: string,
    /** Cached content of file. Dirty if opened in vscode, disk otherwise */
    content: string,
    /** Cached trimmed content of `content`. */
    contentTrimmed: string,
    /** Completion items */
    elements: {
        /** \ref{} items */
        reference?: ReferenceItem[],
        /** \gls items */
        glossary?: GlossaryItem[],
        /** \begin{} items */
        environment?: CmdEnvSuggestion[],
        /** \cite{} items from \bibitem definition */
        bibitem?: CitationItem[],
        /** macro items */
        macro?: CmdEnvSuggestion[],
        /** \usepackage{}, a dictionary whose key is package name and value is the options */
        package?: {[packageName: string]: string[]},
        /** _{} */
        subscripts?: CompletionItem[],
        /** ^{} */
        superscripts?: CompletionItem[]
    },
    /** The sub-files of the LaTeX file. They should be tex or plain files */
    children: {
        /** The index of character sub-content is inserted */
        index: number,
        /** The path of the sub-file */
        filePath: string
    }[],
    /** The array of the paths of `.bib` files referenced from the LaTeX file */
    bibfiles: Set<string>,
    /** A dictionary of external documents provided by `\externaldocument` of
     * `xr` package. The value is its prefix `\externaldocument[prefix]{*}` */
    external: {[filePath: string]: string},
    /** The AST of this file, generated by unified-latex */
    ast?: Ast.Root
}

export type StepQueue = {
    /**
     * The {@link Step}s in the current recipe.
     */
    steps: Step[],
    /**
     * The {@link Step}s in the next recipe to be executed after the current
     * ones.
     */
    nextSteps: Step[]
}

export type ProcessEnv = {
    [key: string]: string | undefined
}

export type Tool = {
    name: string,
    command: string,
    args?: string[],
    env?: ProcessEnv
}

export type Recipe = {
    name: string,
    tools: (string | Tool)[]
}

export type RecipeStep = Tool & {
    rootFile: string,
    recipeName: string,
    timestamp: number,
    index: number,
    isExternal: false,
    isRetry: boolean,
    isSkipped: boolean
}

export type ExternalStep = Tool & {
    rootFile?: string,
    recipeName: 'External',
    timestamp: number,
    index: number,
    isExternal: true,
    cwd: string
}

export type Step = RecipeStep | ExternalStep

export type ViewerMode = 'browser' | 'tab' | 'external' | 'legacy' | 'singleton'

export type SyncTeXRecordToPDF = {
    page: number,
    x: number,
    y: number,
    indicator: boolean
}

export type SyncTeXRecordToTeX = {
    input: string,
    line: number,
    column: number
}

export type SyncTeXRecordToPDFAll = SyncTeXRecordToPDF & {
    h: number,
    v: number,
    W: number,
    H: number
}

export interface LaTeXLinter {
    readonly linterDiagnostics: vscode.DiagnosticCollection,
    getName(): string,
    lintRootFile(rootPath: string): Promise<void>,
    lintFile(document: vscode.TextDocument): Promise<void>,
    parseLog(log: string, filePath?: string): void
}

export interface LaTeXFormatter {
    formatDocument(document: vscode.TextDocument, range?: vscode.Range): Promise<vscode.TextEdit | undefined>
}

export enum TeXElementType { Environment, Macro, Section, SectionAst, SubFile, BibItem, BibField }

export type TeXElement = {
    readonly type: TeXElementType,
    readonly name: string,
    label: string,
    readonly lineFr: number,
    lineTo: number,
    readonly filePath: string,
    children: TeXElement[],
    parent?: TeXElement,
    appendix?: boolean
}

export type TeXMathEnv = {
    texString: string,
    range: vscode.Range,
    envname: string
}

export type PackageRaw = {
    deps: DependencyRaw[],
    macros: MacroRaw[],
    envs: EnvironmentRaw[],
    keys: { [key: string]: string[] },
    args: string[]
}

export type DependencyRaw = {
    name: string,
    if?: string
}

export type EnvironmentRaw = {
    name: string,
    arg?: { format: string, snippet: string, keys?: string[], keyPos?: number },
    if?: string,
    unusual?: boolean
}

export type MacroRaw = {
    name: string,
    arg?: { format: string, snippet: string, keys?: string[], keyPos?: number },
    if?: string,
    unusual?: boolean,
    detail?: string,
    doc?: string
}

export type EnvironmentInfo = EnvironmentRaw & {
    package: string,
    detail: string
}

export type MacroInfo = MacroRaw & {
    package: string
}

export type PackageObsolete = {
    includes: {[key: string]: string[]},
    macros: {[key: string]: any},
    envs: {[key: string]: any},
    options: string[],
    keyvals: string[][]
}

export type CompletionArgs = {
    uri: vscode.Uri,
    langId: string,
    line: string,
    position: vscode.Position
}

export interface CompletionProvider {
    from(result: RegExpMatchArray, args: CompletionArgs): vscode.CompletionItem[]
}

export interface CompletionItem extends vscode.CompletionItem {
    label: string
}

export interface CitationField extends Map<string, string> {
    author?: string,
    journal?: string,
    journaltitle?: string,
    title?: string,
    publisher?: string,
    join(selectedFields: string[], prefixWithKeys: boolean, joinString?: string): string
}

export interface CitationItem extends CompletionItem {
    key: string,
    fields: CitationField,
    file: string,
    position: vscode.Position
}

export enum EnvSnippetType { AsName, AsMacro, ForBegin }

export interface ReferenceItem extends CompletionItem {
    /** The file that defines the ref. */
    file: string,
    /** The position that defines the ref. */
    position: vscode.Position,
    /** Math macros */
    math?: TeXMathEnv,
    /**  Stores the ref number. */
    prevIndex?: {refNumber: string, pageNumber: string}
}

export enum GlossaryType {
    glossary,
    acronym
}

export interface GlossaryItem extends CompletionItem {
    type: GlossaryType,
    filePath: string,
    position: vscode.Position
}
